/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> uls_print.c </file>
  <brief>
    Vararg routines for printing output to files.
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, 2011.
  </author>
*/

#include "common.h"
#include "uls_print.h"

static DECLARE_ULS_MUTEX(sysprn_global_mtx);
static uls_lf_csz_t mb_buf;
static void *sysprn;
static uls_lf_puts_t sysprn_puts;
static int sysprn_opened;

// <brief>
// Opens a file for output.
// After calls of print() or wprint(), be sure to call uls_sysprn_close().
// </brief>
// <parm name="out_file">The output file path</parm>
// <return>none</return>
void uls_sysprn_open(void* data, uls_lf_puts_t proc)
{
	if (proc == NULL) {
		if (data == NULL) {
			proc = uls_lf_puts_null;
		} else {
			proc = uls_lf_puts_file;
		}
	}

	uls_lock_mutex(&sysprn_global_mtx);

	if (sysprn_opened) {
		uls_unlock_mutex(&sysprn_global_mtx);
	} else {
		sysprn_opened = 1;
		sysprn = data;
		sysprn_puts = proc;
	}
}

// <brief>
// closes the output file used by uls_print() or uls_wprint().
// </brief>
// <return>none</return>
void uls_sysprn_close(void)
{
	sysprn = (void *) stdout;
	sysprn_puts = uls_lf_puts_file;
	sysprn_opened = 0;
	uls_unlock_mutex(&sysprn_global_mtx);
}

void initialize_sysprn(void)
{
	uls_init_mutex(&sysprn_global_mtx);
	sysprn = (void *) stdout;
	sysprn_puts = uls_lf_puts_file;
	sysprn_opened = 0;

	uls_lf_csz_init(&mb_buf, NULL);
}

void finalize_sysprn(void)
{
	uls_lf_csz_deinit(&mb_buf);
	uls_deinit_mutex(&sysprn_global_mtx);
}

int uls_vprint(const uls_lf_char_t* fmt, va_list args)
{
	return uls_lf_vxprintf_generic(NULL, sysprn, sysprn_puts, NULL, fmt, args);
}

// <brief>
// This will print the formatted string to the standard oupput, by default.
// But you can change the output port by calling 
// </brief>
// <parm name="fmt">The template for message string</parm>
// <return># of chars printed</return>
int uls_print(const uls_lf_char_t* fmt, ...)
{
	va_list	args;
	int len;

	va_start(args, fmt);
	len = uls_vprint(fmt, args);
	va_end(args);

	return len;
}

// <brief>
// Prints the wide string formatted by variable arguments in the varargs(stdarg) style.
// </brief>
// <parm name="wfmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <parm name="p_wlen">a out-parameter, the length of returning string, # of wchar_t's</parm>
// <return>The formatted wide string</return>
static uls_lf_word_t*
uls_vwprint(const uls_lf_word_t* wfmt, va_list args, int* p_wlen)
{
	uls_lf_word_t *wptr;
	char *ptr;
	int len, len2;
	size_t siz;

	uls_lf_csz_reset(&mb_buf, NULL);
	len = uls_lf_vzwprintf(NULL, &mb_buf, NULL, wfmt, args);

	if ((siz=mbstowcs(NULL, (char *) csz_text(&mb_buf.csz), 0)) == (size_t) -1) {
		return NULL;
	}

	csz_add_eos(&mb_buf.csz);
	len2 = uls_roundup(csz_length(&mb_buf.csz), sizeof(uls_lf_word_t));

	csz_append(&mb_buf.csz, NULL, (len2-len) + (siz + 1) *sizeof(uls_lf_word_t));

	ptr = (char *) csz_text(&mb_buf.csz);
	wptr = (uls_lf_word_t *) (ptr + len2);

	if ((siz = mbstowcs(wptr, ptr, siz + 1)) == (size_t) -1) {
		*p_wlen = -1;
		wptr = NULL;
	} else {
		*p_wlen = (int) siz;
	}

	return wptr;
}

// <brief>
// An wide string version of the method 'print()'
// </brief>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's</return>
int uls_wprint(const uls_lf_word_t* wfmt, ...)
{
	va_list	args;
	uls_lf_word_t *wptr;
	int wlen;

	va_start(args, wfmt);
	wptr = uls_vwprint(wfmt, args, &wlen);
	sysprn_puts(sysprn, (uls_lf_char_t *) wptr, wlen*sizeof(uls_lf_word_t));
	va_end(args);

	return wlen;
}

// <brief>
// This stores the formatted string to the 'buf'.
// </brief>
// <parm name="buf">The output buffer for the formatted string</parm>
// <parm name="bufsiz">The size of 'buf'</parm>
// <parm name="fmt">The template for message string</parm>
// <return># of chars filled except for '\0'</return>
int
uls_snprintf(uls_lf_char_t* buf, int bufsiz, const uls_lf_char_t *fmt, ...)
{
	buf4legacy_t stdbuf;
	va_list args;
	int len;

	if (bufsiz <= 1) {
		if (bufsiz==1) {
			buf[0] = '\0';
		}
		return 0;
	}

	stdbuf.flags = 0;
	stdbuf.bufptr = stdbuf.buf = buf;
	stdbuf.buflen = bufsiz - 1;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(NULL, &stdbuf, uls_lf_puts_str, NULL, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// Stores a wide string formatted by the string 'wfmt' with 'args' to 'wbuf'.
// </brief>
// <parm name="wbuf">The buffer of wchar_t to store the result.</parm>
// <parm name="wbuf_siz">The size of 'wbuf', # of wchar_t's</parm>
// <parm name="wfmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <return># of wchar_t's int 'wbuf'</return>
int
uls_snvwprintf(uls_lf_word_t* wbuf, int wbuf_siz, const uls_lf_word_t *wfmt, va_list args)
{
	size_t wlen;
	int len;

	if (wbuf_siz <= 1) {
		if (wbuf_siz==1) wbuf[0] = L'\0';
		return wbuf_siz;
	}

	uls_lf_csz_reset(&mb_buf, NULL);
	len = uls_lf_vzwprintf(NULL, &mb_buf, NULL, wfmt, args);

	if ((wlen=mbstowcs(wbuf, (char *) csz_text(&mb_buf.csz), wbuf_siz)) == (size_t) -1) {
		len = -1;
	} else {
		len = (int) wlen;
	}

	return len;
}

// <brief>
// An wide string version of the method 'snprintf()'
// </brief>
// <parm name="wbuf">The output buffer for the formatted string</parm>
// <parm name="wbufsiz">The size of 'buf', # of wchar_t's</parm>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's</return>
int
uls_snwprintf(uls_lf_word_t* wbuf, int wbuf_siz, const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	int wlen;

	va_start(args, wfmt);
	wlen = uls_snvwprintf(wbuf, wbuf_siz, wfmt, args);
	va_end(args);

	return wlen;
}

// <brief>
// Stores a 'char' string formatted by the string 'fmt' with 'args' to 'buf'.
// </brief>
// <parm name="buf">The buffer of char to store the result.</parm>
// <parm name="fmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <return># of chars in 'buf'</return>
static int
uls_vsprintf(uls_lf_char_t *buf, const uls_lf_char_t *fmt, va_list args)
{
	buf4legacy_t stdbuf;

	stdbuf.flags = 0;
	stdbuf.bufptr = stdbuf.buf = buf;
	stdbuf.buflen = (~0U) >> 1;

	return uls_lf_vxprintf_generic(NULL, &stdbuf, uls_lf_puts_str, NULL, fmt, args);
}

// <brief>
// This stores the formatted string to the 'buf' with its size unknown.
// I recommend not to use this but snprintf().
// </brief>
// <parm name="buf">The output buffer for the formatted string</parm>
// <parm name="fmt">The template for message string</parm>
// <return># of chars filled</return>
int
uls_sprintf(uls_lf_char_t * buf, const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_vsprintf(buf, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'sprintf()'
// </brief>
// <parm name="wbuf">The output buffer for the formatted string</parm>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's written</return>
int
uls_swprintf(uls_lf_word_t * wbuf, const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	int wlen;

	va_start(args, wfmt);
	wlen = uls_snvwprintf(wbuf, (~0U) >> 1, wfmt, args);
	va_end(args);

	return wlen;
}

int
uls_vfprintf(FILE* fp, const uls_lf_char_t *fmt, va_list args)
{
	return uls_lf_vxprintf_generic(NULL, fp, uls_lf_puts_file, NULL, fmt, args);
}

// <brief>
// This stores the formatted string to the 'fp', FILE*.
// </brief>
// <parm name="fp">The pointer of FILE to be wrriten</parm>
// <parm name="fmt">The template for message string</parm>
// <return># of chars written</return>
int
uls_fprintf(FILE* fp, const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_vfprintf(fp, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'fprintf()'
// </brief>
// <parm name="fp">The pointer of FILE to be wrriten</parm>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's written</return>
int
uls_fwprintf(FILE* fp, const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	uls_lf_word_t *wptr;
	int wlen;

	va_start(args, wfmt);
	wptr = uls_vwprint(wfmt, args, &wlen);
	uls_lf_puts_file(fp, (uls_lf_char_t *) wptr, wlen*sizeof(uls_lf_word_t));
	va_end(args);

	return wlen;
}

// <brief>
// This prints the formatted string to the 'stdout'.
// </brief>
// <parm name="fmt">The template for message string</parm>
// <return># of chars printed</return>
int
uls_printf(const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(NULL, stdout, uls_lf_puts_file, NULL, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'uls_printf()'
// </brief>
// <parm name="wfmt">The template for message string</parm>
// <return># of chars printed</return>
int
uls_wprintf(const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	int wlen;

	va_start(args, wfmt);
	wlen = uls_lf_vsnwprintf_generic(NULL, stdout, uls_lf_puts_file, NULL, wfmt, args);
	va_end(args);

	return wlen;
}
